package me.timlong.controller;

import com.alibaba.druid.util.StringUtils;
import com.google.common.util.concurrent.RateLimiter;
import me.timlong.error.BusinessException;
import me.timlong.error.EmBusinessError;
import me.timlong.mq.MqProducer;
import me.timlong.response.CommonReturnType;
import me.timlong.service.ItemService;
import me.timlong.service.OrderService;
import me.timlong.service.PromoService;
import me.timlong.service.model.UserModel;
import me.timlong.util.CodeUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;

import javax.annotation.PostConstruct;
import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.awt.image.RenderedImage;
import java.io.IOException;
import java.util.Map;
import java.util.concurrent.*;

@Controller("order")
@RequestMapping("/order")
@CrossOrigin(allowCredentials = "true", allowedHeaders = "*")
public class OrderController extends BaseController {

    @Autowired
    private OrderService orderService;

    @Autowired
    private HttpServletRequest httpServletRequest;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private MqProducer mqProducer;

    @Autowired
    private ItemService itemService;

    @Autowired
    private PromoService promoService;

    private ExecutorService executorService;

    // GUAVA 限流数据结构
    private RateLimiter orderRateLimiter;

    @PostConstruct
    public void init() {
        // 长度为20的线程池，使用LinkedBlockingQueue维护，拥塞控制
        executorService = Executors.newFixedThreadPool(20);

        // 设置300的tps
        orderRateLimiter = RateLimiter.create(300);
    }


    @RequestMapping(value = "/generateverifycode", method = {RequestMethod.GET, RequestMethod.POST})
    @ResponseBody
    public void generateVerifyCode(HttpServletResponse response) throws BusinessException, IOException {
        String token = httpServletRequest.getParameterMap().get("token")[0];
        if(StringUtils.isEmpty(token)) {
            throw new BusinessException(EmBusinessError.USER_NOT_LOGIN, "用户未登录，不能获取验证码");
        }
        UserModel userModel = (UserModel) redisTemplate.opsForValue().get(token);

        // 获取用户的登录信息
        if (null== userModel) { // 这种情况下，会话过期
            throw new BusinessException(EmBusinessError.USER_NOT_LOGIN, "用户未登录，不能获取验证码");
        }

        Map<String,Object> map = CodeUtil.generateCodeAndPic();
        redisTemplate.opsForValue().set("verify_code_" + userModel.getId(), map.get("code"));
        redisTemplate.expire("verify_code_" + userModel.getId(), 10, TimeUnit.MINUTES);
        ImageIO.write((RenderedImage) map.get("codePic"), "jpeg", response.getOutputStream());
    }



    @RequestMapping(value = "/generatetoken", method = {RequestMethod.POST}, consumes = {CONTENT_TYPE_FORMED})
    @ResponseBody
    public CommonReturnType generateToken(@RequestParam(name = "itemId") Integer itemId,
                                          @RequestParam(name = "promoId", required = true) Integer promoId,
                                          @RequestParam(name = "verifyCode") String verifyCode) throws BusinessException {

        String token = httpServletRequest.getParameterMap().get("token")[0];
        if(StringUtils.isEmpty(token)) {
            throw new BusinessException(EmBusinessError.USER_NOT_LOGIN, "用户未登录，不能下单");
        }
        UserModel userModel = (UserModel) redisTemplate.opsForValue().get(token);

        // 获取用户的登录信息
        if (null== userModel) { // 这种情况下，会话过期
            throw new BusinessException(EmBusinessError.USER_NOT_LOGIN, "用户未登录，不能下单");
        }

        // 验证码校验，防刷限流
        String redisVerifyCode = (String) redisTemplate.opsForValue().get("verify_code_" + userModel.getId());
        if(StringUtils.isEmpty(redisVerifyCode)) {
            throw new BusinessException(EmBusinessError.PARAMETER_VALIDATION_ERROR, "请求非法");
        }

        if(!redisVerifyCode.equalsIgnoreCase(verifyCode)) {
            throw new BusinessException(EmBusinessError.PARAMETER_VALIDATION_ERROR, "请求非法，验证码错误");

        }


        // 限制令牌发放
        Long result = redisTemplate.opsForValue().increment("promo_door_count_" + promoId, -1);
        if(result < 0) {
            return null;
        }

        String promoToken = promoService.generateSecondKillToken(promoId, userModel.getId(), itemId);
        if(null == promoToken) {
            throw new BusinessException(EmBusinessError.PARAMETER_VALIDATION_ERROR, "生成秒杀令牌失败！");
        }
        return CommonReturnType.create(promoToken);
    }

    @RequestMapping(value = "/createorder", method = {RequestMethod.POST}, consumes = {CONTENT_TYPE_FORMED})
    @ResponseBody
    public CommonReturnType createOrder(@RequestParam(name = "itemId") Integer itemId,
                                        @RequestParam(name = "amount") Integer amount,
                                        @RequestParam(name = "promoId", required = false) Integer promoId,
                                        @RequestParam(name = "promoToken", required = false) String promoToken) throws BusinessException {


        if(!orderRateLimiter.tryAcquire()) {
            throw new BusinessException(EmBusinessError.RATE_LIMIT);
        }
        String userLoginToken = httpServletRequest.getParameterMap().get("userLoginToken")[0];
        if(StringUtils.isEmpty(userLoginToken)) {
            throw new BusinessException(EmBusinessError.USER_NOT_LOGIN, "用户未登录，不能下单");
        }
        UserModel userModel = (UserModel) redisTemplate.opsForValue().get(userLoginToken);

        // 获取用户的登录信息
        if (null== userModel) { // 这种情况下，会话过期
            throw new BusinessException(EmBusinessError.USER_NOT_LOGIN, "用户未登录，不能下单");
        }

        //校验用户的秒杀令牌
        if(null != promoId) {
            String inRedisPromoToken = (String) redisTemplate.opsForValue().get("promo_token_" + promoId + "_userid_" + userModel.getId() + "_itemid_" + promoId);
            if(null == inRedisPromoToken)
                throw new BusinessException(EmBusinessError.PARAMETER_VALIDATION_ERROR, "秒杀令牌校验失败");

            if(!StringUtils.equals(inRedisPromoToken, promoToken)) {
                throw new BusinessException(EmBusinessError.PARAMETER_VALIDATION_ERROR, "秒杀令牌校验失败");
            }
        }


        // 同步调用线程池的submit方法
        Future<?> future = executorService.submit(new Callable<Object>() {
            @Override
            public Object call() throws Exception {
                // 加入库存流水init状态
                String stockLogId = itemService.initStockLog(itemId, amount);

                // OrderModel orderModel = orderService.createOrder(userModel.getId(), itemId, promoId, amount);
                // 这里将上一句话替换为RocketMq的事务型订单创建，通过MQProducer实现
                boolean result = mqProducer.transactionAsyncReduceStock(userModel.getId(), promoId, itemId, amount, stockLogId);
                if (result) {
                    throw new BusinessException(EmBusinessError.UNKNOWN_ERROR, "下单失败！");
                }
                return null;
            }
        });

        try {
            future.get();
        } catch (InterruptedException | ExecutionException e) {
            throw new BusinessException(EmBusinessError.UNKNOWN_ERROR);
        }


        return CommonReturnType.create(null);
    }


}
